﻿Imports System
Imports System.Threading
Imports System.Collections.Generic

Imports NoDeadLockReadWriteLocker

Public Class ThreadSafeList(Of T)
	Implements IList(Of T), ISimpleReadWriteLocker

	#Region "Classes"
		Private Class ModificationCache
			#Region "Structures"
        Private Enum ModificationType
					Added
					Removed
					Cleared
				End Enum

				Private Structure Item
					Public Type  As ModificationType
					Public Item  As T
					Public Index As Integer

					Public Sub New(__type As ModificationType, __item As T, __index As Integer)
						Type  = __type
						Item  = __item
						Index = __index
					End Sub
				End Structure
			#End Region

			#Region "Members"
				' List to apply changes, when requested.
				Private _List  As List(Of T)

				' Modifications being tracked.
				Private _Items As New List(Of Item)()
			#End Region

			#Region "Constructors"
				Public Sub New(__list As List(Of T))
					_List = __list
				End Sub
			#End Region

			#Region "Methods"
				Public Sub Insert(index As Integer, item As T)
					_Items.Add(New Item(ModificationType.Added, item, index))
				End Sub

				Public Sub Add(item As T)
					_Items.Add(New Item(ModificationType.Added, item, -1))
				End Sub

				Public Sub Assign(srcItem As T, destItem As T)
					_Items.Add(New Item(ModificationType.Removed, srcItem, -1))
					_Items.Add(New Item(ModificationType.Added, destItem, _List.IndexOf(srcItem)))
				End Sub

				Public Sub Move(item As T, destIndex As Integer)
					_Items.Add(New Item(ModificationType.Removed, item, -1))
					_Items.Add(New Item(ModificationType.Added, item, destIndex))
				End Sub

				Public Sub Move(srcIndex As Integer, destIndex As Integer)
					Move(_List(srcIndex), destIndex)
				End Sub

				Public Sub Swap(lhsIndex As Integer, rhsIndex As Integer)
					Move(lhsIndex, rhsIndex)
					Move(rhsIndex, lhsIndex)
				End Sub

				Public Sub Remove(item As T)
					_Items.Add(New Item(ModificationType.Removed, item, -1))
				End Sub

				Public Sub RemoveAt(index As Integer)
					_Items.Add(New Item(ModificationType.Removed, _List(index), -1))
				End Sub

				Public Sub Clear()
					_Items.Clear()
					_Items.Add(New Item(ModificationType.Cleared, Nothing, -1))
				End Sub

				Public Sub PerformCachedModifications()
					If _Items.Count > 0 Then
						For Each Item As Item In _Items
							Select Case Item.Type 
								Case ModificationType.Added
									If Item.Index = -1 OrElse Item.Index > _List.Count Then
										_List.Add(Item.Item)
									Else
										_List.Insert(Item.Index, Item.Item)
									End If
								Case ModificationType.Removed
									_List.Remove(Item.Item)
								Case ModificationType.Cleared
									_List.Clear()
							End Select
						Next

						_Items.Clear()
					End If
				End Sub
			#End Region
		End Class

		Private Class ThreadSafeIterator
			Implements IEnumerator(Of T)

			#Region "Members"
				Private _Iterator   As IEnumerator(Of T)
				Private _ReadLock   As IDisposable 
				Private _IsDisposed As Boolean = False
			#End Region

			#Region "Constructors"
				Public Sub New(__iterator As IEnumerator(Of T), __readLockObject As IDisposable)
					_Iterator = __iterator
					_ReadLock = __readLockObject
				End Sub
			#End Region

			#Region "Properties"
				Public ReadOnly Property Current As T _
					Implements IEnumerator(Of T).Current

					Get
						Return _Iterator.Current
					End Get
				End Property

				Private ReadOnly Property CurrentValue As Object _
					Implements IEnumerator.Current

					Get
						Return DirectCast(_Iterator, IEnumerator).Current
					End Get
				End Property
			#End Region

			#Region "Methods"
				Public Function MoveNext() As Boolean _
					Implements IEnumerator.MoveNext

					Return _Iterator.MoveNext()
				End Function

				Public Sub Reset() _
					Implements IEnumerator.Reset

					_Iterator.Reset()
				End Sub

				Protected Sub Dispose(ByVal disposing As Boolean)
					If Not Me._IsDisposed Then
						If disposing Then
							_Iterator.Dispose()
							_Iterator = Nothing

							_ReadLock.Dispose()
							_ReadLock = Nothing
						End If

						' TODO: free your own state (unmanaged objects).
						' TODO: set large fields to null.
					End If

					_IsDisposed = True
				End Sub

				Public Sub Dispose() _
					Implements IDisposable.Dispose

					' Do not change this code or add code to this method.
					Dispose(True)
					GC.SuppressFinalize(Me)
				End Sub
			#End Region
		End Class
	#End Region

	#Region "Members"
		WithEvents Private _Locker As ReadWriteLocker

		Private _List              As List(Of T)
		Private _ModificationCache As ModificationCache
	#End Region

	#Region "Constructors"
		Public Sub New(Optional __isThreadSafe As Boolean = False)
			Dim List As New List(Of T)()

			_Locker            = New ReadWriteLocker(ReadWriteLocker.Behavior.AllowDeadLocks)
			_List              = List
			_ModificationCache = New ModificationCache(List)
		End Sub

		Public Sub New(__behavior As ReadWriteLocker.Behavior, _
		               Optional __isThreadSafe As Boolean = False)

			Dim List As New List(Of T)()

			_Locker            = New ReadWriteLocker(__behavior)
			_List              = List
			_ModificationCache = New ModificationCache(List)
		End Sub

		Public Sub New(__capacity As Integer, Optional __isThreadSafe As Boolean = False)
			Dim List As New List(Of T)(__capacity)

			_Locker            = New ReadWriteLocker(ReadWriteLocker.Behavior.AllowDeadLocks)
			_List              = List
			_ModificationCache = New ModificationCache(List)
		End Sub

		Public Sub New(__behavior As ReadWriteLocker.Behavior, _
		               __capacity As Integer,                  _
		               Optional __isThreadSafe As Boolean = False)

			Dim List As New List(Of T)(__capacity)

			_Locker            = New ReadWriteLocker(__behavior)
			_List              = List
			_ModificationCache = New ModificationCache(List)
		End Sub

		Public Sub New(__enumerable As IEnumerable(Of T), Optional __isThreadSafe As Boolean = False)
			Dim List As New List(Of T)(__enumerable)

			_Locker            = New ReadWriteLocker(ReadWriteLocker.Behavior.NoDeadLocks)
			_List              = List
			_ModificationCache = New ModificationCache(List)
		End Sub

		Public Sub New(__enumerable As IEnumerable(Of T),      _
		               __behavior As ReadWriteLocker.Behavior, _
		               Optional __isThreadSafe As Boolean = False)

			Dim List As New List(Of T)(__enumerable)

			_Locker            = New ReadWriteLocker(__behavior)
			_List              = List
			_ModificationCache = New ModificationCache(List)
		End Sub
	#End Region

	#Region "Properties"
		Public ReadOnly Property IsReadOnly As Boolean _
			Implements ICollection(Of T).IsReadOnly

			Get
				Return False
			End Get
		End Property

    Public ReadOnly Property IsReading As Boolean _
			Implements ISimpleReadWriteLocker.IsReading

			Get
				Return _Locker.IsReading
			End Get
		End Property

    Public ReadOnly Property IsWriting As Boolean _
			Implements ISimpleReadWriteLocker.IsWriting

			Get
				Return _Locker.IsWriting
			End Get
		End Property

		Public ReadOnly Property IsThreadSafe As Boolean _
			Implements ISimpleReadWriteLocker.IsThreadSafe

			Get
				Return _Locker.IsThreadSafe
			End Get
		End Property

		Public ReadOnly Property Count As Integer _
			Implements ICollection(Of T).Count

			Get
				Using ReadLock As IDisposable = _Locker.GetReadLock()
					Return _List.Count
				End Using
			End Get
		End Property

		Public Default Property Item(index As Integer) As T _
			Implements IList(Of T).Item

			Get
				Using ReadLock As IDisposable = _Locker.GetReadLock()
					Return _List.Item(index)
				End Using
			End Get
			Set
				Using WriteLock As IDisposable = _Locker.GetWriteLock()
					If _Locker.IsReading Then
						_ModificationCache.Assign(_List(index), Value)
					Else
						_List(index) = Value
					End If
				End Using
			End Set
		End Property
	#End Region

	#Region "Members"
		Public Function Contains(item As T) As Boolean _
			Implements ICollection(Of T).Contains

			Using ReadLock As IDisposable = _Locker.GetReadLock()
				Return _List.Contains(item)
			End Using
		End Function

		Public Function IndexOf(item As T) As Integer _
			Implements IList(Of T).IndexOf

			Using ReadLock As IDisposable = _Locker.GetReadLock()
				Return _List.IndexOf(item)
			End Using
		End Function

		Public Sub CopyTo(array() As T, arrayIndex As Integer) _
			Implements ICollection(Of T).CopyTo

			Using ReadLock As IDisposable = _Locker.GetReadLock()
				_List.CopyTo(array, arrayIndex)
			End Using
		End Sub

		Public Function GetEnumerator() As IEnumerator(Of T) _
			Implements IEnumerable(Of T).GetEnumerator

			' Returns a thread-safe iterator.
			Return New ThreadSafeIterator(_List.GetEnumerator(), _Locker.GetReadLock())
		End Function

		Private Function GetValueEnumerator() As IEnumerator _
			Implements IEnumerable.GetEnumerator

			Return GetEnumerator()
		End Function

		Public Sub Insert(index As Integer, item As T) _
			Implements IList(Of T).Insert

			Using WriteLock As IDisposable = _Locker.GetWriteLock()
				If _Locker.IsReading() Then
					_ModificationCache.Insert(index, item)
				Else
					_List.Insert(index, item)
				End If
			End Using
		End Sub

		Public Sub Add(item As T) _
			Implements ICollection(Of T).Add

			Using WriteLock As IDisposable = _Locker.GetWriteLock()
				If _Locker.IsReading() Then
					_ModificationCache.Add(item)
				Else
					_List.Add(item)
				End If
			End Using
		End Sub

		Public Sub RemoveAt(index As Integer) _
			Implements IList(Of T).RemoveAt

			Using WriteLock As IDisposable = _Locker.GetWriteLock()
				If _Locker.IsReading() Then
					_ModificationCache.RemoveAt(index)
				Else
					_List.RemoveAt(index)
				End If
			End Using
		End Sub

		Public Function Remove(item As T) As Boolean _
			Implements ICollection(Of T).Remove

			Using WriteLock As IDisposable = _Locker.GetWriteLock()
				If _Locker.IsReading() Then
					_ModificationCache.Remove(item)
				Else
					_List.Remove(item)
				End If
			End Using
		End Function

		Public Sub Clear() _
			Implements ICollection(Of T).Clear

			Using WriteLock As IDisposable = _Locker.GetWriteLock()
				If _Locker.IsReading() Then
					_ModificationCache.Clear()
				Else
					_List.Clear()
				End If
			End Using
		End Sub

		Public Function GetReadLock() As IDisposable _
			Implements ISimpleReadWriteLocker.GetReadLock

			Return _Locker.GetReadLock()
		End Function

		Public Function GetWriteLock() As IDisposable _
			Implements ISimpleReadWriteLocker.GetWriteLock

			Return _Locker.GetWriteLock()
		End Function

		Public Function TryGetReadLock() As IDisposable _
			Implements ISimpleReadWriteLocker.TryGetReadLock

			Return _Locker.TryGetReadLock()
		End Function

		Public Function TryGetWriteLock() As IDisposable _
			Implements ISimpleReadWriteLocker.TryGetWriteLock

			Return _Locker.TryGetWriteLock()
		End Function

		Public Sub Dispose() _
			Implements ISimpleReadWriteLocker.Dispose

			_Locker.Dispose()
		End Sub
	#End Region

	#Region "Events"
		Protected Overridable Sub OnUnlocked() _
			Handles _Locker.UnlockedForReading

			Using WriteLock As IDisposable = _Locker.GetWriteLock()
				If Not _Locker.IsReading Then
					_ModificationCache.PerformCachedModifications()
				End If
			End Using
		End Sub
	#End Region
End Class
